-
Notifications
You must be signed in to change notification settings - Fork 764
WEB-117 Loan Account Charge(Amount% charges) Doubling on Application Modification #2690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughDeduplicates loan charges during charges-step initialization and before submitting edits. Charges are normalized and deduped by Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant C as LoansAccountChargesStepComponent
participant E as EditLoansAccountComponent
participant API as Backend
U->>C: Open or edit loan account
Note over C: Initialize charges\n• Use product template, or\n• if modifying and template present, use account template
C->>C: getUniqueCharges(charges)\n(deduplicate by chargeId)
U->>E: Submit edited loan account
E->>E: Build uniqueCharges map\n(skip null/undefined chargeId, normalize entries)
E->>API: POST payload with deduplicated `charges`
API-->>E: Response
E-->>U: Confirmation / result
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/app/loans/edit-loans-account/edit-loans-account.component.ts (2)
141-145: Include feeOnMonthDay when present to avoid losing Annual Fee datesThe step component sets feeOnMonthDay for 'Annual Fee'. The payload currently only sends dueDate, which may drop that info.
- charges: Array.from(uniqueCharges.values()).map((charge: any) => ({ - chargeId: charge.id || charge.chargeId, - amount: charge.amount, - dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat) - })), + charges: Array.from(uniqueCharges.values()).map((charge: any) => { + const payload: any = { + chargeId: charge.id ?? charge.chargeId, + amount: charge.amount + }; + if (charge.dueDate) { + payload.dueDate = this.dateUtils.formatDate(charge.dueDate, dateFormat); + } + if (charge.feeOnMonthDay) { + payload.feeOnMonthDay = this.dateUtils.formatDate(charge.feeOnMonthDay, dateFormat); + } + if (charge.feeInterval != null) { + payload.feeInterval = charge.feeInterval; + } + return payload; + }),Please confirm Fineract expects feeOnMonthDay/feeInterval for your charge types in PUT /v1/loans. If not, we can pare this back. Based on learnings.
132-137: Avoid double-dedup: rely on a single, shared helper to keep behavior consistentWe dedup in the step getter and again here. Prefer one source of truth (e.g., a shared util), and keep a server-side guard (filter invalids) here.
I can extract a shared function (e.g., loans-charge-utils.ts) and update both call sites. Want me to draft it?
Also applies to: 141-145
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts (3)
165-176: Prefer existing loan charges on modification; current order may transiently show product chargesOnChanges runs before OnInit; with empty chargesDataSource, the first branch loads product charges even in edit mode, then OnInit overwrites with existing charges. This causes redundant work and potential UI flicker.
- const isModification = this.loanId != null; - if ( - this.loansAccountProductTemplate.charges && - this.loansAccountProductTemplate.charges.length > 0 && - this.chargesDataSource.length === 0 - ) { - this.chargesDataSource = - this.loansAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || []; - } else if (isModification && this.loansAccountTemplate && this.loansAccountTemplate.charges) { - this.chargesDataSource = - this.loansAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || []; - } + const isModification = this.loanId != null; + if (isModification && this.loansAccountTemplate?.charges?.length) { + this.chargesDataSource = + this.loansAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || []; + } else if (this.chargesDataSource.length === 0 && this.loansAccountProductTemplate?.charges?.length) { + this.chargesDataSource = + this.loansAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || []; + }Follow-up: consider moving init from ngOnInit to ngOnChanges and deleting the ngOnInit block that sets chargesDataSource to avoid duplication. Based on coding guidelines.
320-323: Confirm business rule: should duplicate chargeId entries ever be allowed?If some charge types legitimately appear multiple times (e.g., specified due dates), hard dedup by id will drop them. If duplicates are only the bug scenario (WEB-117), proceed; otherwise, key by a composite (id + dueDate) when the chargeTimeType allows multiples.
I can implement conditional dedup (by id, or by id+date when charge.allowMultiple) if you confirm the rule.
141-146: *Add a trackBy function to the charges table’s ngFor
Define and reference a trackBy inloans-account-charges-step.component.html(e.g.<tr *ngFor="let charge of chargesDataSource; trackBy: trackByChargeId">) and add to the component:trackByChargeId(_: number, charge: any) { return charge.id; }This ensures row identity is preserved after array updates, avoiding unnecessary DOM churn.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/loans/edit-loans-account/edit-loans-account.component.ts(1 hunks)src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**
⚙️ CodeRabbit configuration file
src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.
Files:
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.tssrc/app/loans/edit-loans-account/edit-loans-account.component.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run Lint, Build and Deploy
src/app/loans/edit-loans-account/edit-loans-account.component.ts
Outdated
Show resolved
Hide resolved
...ans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts
Show resolved
Hide resolved
097e45d to
f31b61e
Compare
|
@steinwinde Please Review . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is not a right solution
| const loanType = 'individual'; | ||
| const uniqueCharges = new Map<number | string, any>(); | ||
| (this.loansAccount.charges ?? []).forEach((charge: any) => { | ||
| const chargeId = charge.id ?? charge.chargeId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we are doing this? I mean the Charge has an attribute for the Id and that's all
|
@alberto-art3ch Initially, I had fixed the charge doubling issue by using a Map to track unique charges based on both id and chargeId fields. After that coderabbit has review the code and it suggested to add proper typing and null checking to handle edege cases . |
|
@IOhacker , @steinwinde , @alberto-art3ch Please Review . |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
|
@coderabbitai is this correct solution? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 19970 @gkbishnoi07, the solution partially addresses the issue but has some concerns: Issues with Current Implementation
Recommendations
The solution will prevent duplicate charges from being submitted, but understanding and fixing the root cause would be more robust. Would you like me to help investigate where the duplication originates? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please review my comments
src/app/loans/edit-loans-account/edit-loans-account.component.ts
Outdated
Show resolved
Hide resolved
f31b61e to
1430416
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts (1)
358-370: Deduplication key should fallback toidto matchaddChargelogic.Line 362 uses only
charge.chargeIdwithout fallback. This will skip charges that haveidbut nochargeId, causing inconsistency withaddCharge(line 201) which usesidas fallback.Apply this diff to align with
addChargeand handle both fields:private getUniqueCharges<T extends { id?: number | string; chargeId?: number | string }>(charges: T[]): T[] { const uniqueChargesMap = new Map<number | string, T>(); for (const charge of charges ?? []) { - const chargeId = charge.chargeId; + const chargeId = charge.chargeId ?? charge.id; if (chargeId == null) { continue; } uniqueChargesMap.set(chargeId, charge); } return Array.from(uniqueChargesMap.values()); }This ensures charges with only
id(or onlychargeId) are not silently dropped during deduplication.
🧹 Nitpick comments (3)
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts (3)
143-149: Redundant property assignments after spread.The spread operator
...chargealready includesidandchargeId, so explicitly setting them again has no effect unless the values are being transformed.Apply this diff to simplify:
- this.chargesDataSource = - this.loansAccountTemplate.charges.map((charge: any) => { - return { - ...charge, - id: charge.id, - chargeId: charge.chargeId - }; - }) || []; + this.chargesDataSource = this.loansAccountTemplate.charges || [];Or, if the intent is normalization (ensuring both fields exist), make it explicit:
this.chargesDataSource = this.loansAccountTemplate.charges.map((charge: any) => { return { ...charge, - id: charge.id, - chargeId: charge.chargeId + chargeId: charge.chargeId ?? charge.id, + id: charge.id ?? charge.chargeId }; }) || [];
199-203: Use nullish coalescing for safer fallback.Line 201 uses
||which treats falsy values (0, empty string) as missing. Use??to only fallback when null/undefined:const newCharge = { ...charge.value, - chargeId: charge.value.id || charge.value.chargeId + chargeId: charge.value.chargeId ?? charge.value.id };Note: This makes
chargeIdthe primary key (falling back toid), which aligns withgetUniqueChargesat line 362 that keys bychargeId.
67-82: Consider defining a Charge interface for type safety.The component uses
anythroughout for charge objects. Defining a typed interface would improve type safety and developer experience.As per coding guidelines.
Example:
interface LoanCharge { id?: number; chargeId?: number; name?: string; amount?: number; dueDate?: string; feeInterval?: number; feeOnMonthDay?: string; chargeCalculationType?: any; chargeTimeType?: { value: string }; // ... other properties }Then update:
- chargesDataSource: {}[] = []; + chargesDataSource: LoanCharge[] = [];
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/loans/edit-loans-account/edit-loans-account.component.ts(1 hunks)src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/loans/edit-loans-account/edit-loans-account.component.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/app/**
⚙️ CodeRabbit configuration file
src/app/**: For Angular code: verify component separation, trackBy on *ngFor,
strict type safety, and clean observable patterns.
Files:
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts
🔇 Additional comments (1)
src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts (1)
171-191: Verify ngOnChanges doesn't introduce duplicates across lifecycle.The conditional logic populates
chargesDataSourcefrom different sources depending on modification mode and whether the array is empty. SincengOnChangescan fire multiple times, ensure that:
- The
chargesDataSource.length === 0check reliably prevents re-population- Both template sources don't contain overlapping charges
- The modification branch (lines 182-191) doesn't duplicate charges already populated in the first branch
Additionally, lines 184-190 have the same redundancy as noted in
ngOnInit:- this.chargesDataSource = - this.loansAccountTemplate.charges.map((charge: any) => { - return { - ...charge, - id: charge.id, - chargeId: charge.chargeId - }; - }) || []; + this.chargesDataSource = this.loansAccountTemplate.charges || [];Or normalize if both fields might not exist:
this.chargesDataSource = this.loansAccountTemplate.charges.map((charge: any) => { return { ...charge, - id: charge.id, - chargeId: charge.chargeId + chargeId: charge.chargeId ?? charge.id, + id: charge.id ?? charge.chargeId }; }) || [];
|
@alberto-art3ch I have made changes and updated the PR please review . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
@alberto-art3ch Thank you for the review . |
Changes Made :-
-Fixed issue where charges were doubling when editing a loan application.
-Added code to prevent duplicate charges by checking for existing ones.
-Updated how charges are handled during loan modifications to maintain correct values.
WEB-117
Summary by CodeRabbit
Bug Fixes
Improvements